#include "nemu.h"
#include "memory/mmu.h"
#include "device/mmio.h"

#define PMEM_SIZE (128 * 1024 * 1024)

#define pmem_rw(addr, type) *(type *)({\
    Assert(addr < PMEM_SIZE, "physical address(0x%08x) is out of bound", addr); \
    guest_to_host(addr); \
    })

uint8_t pmem[PMEM_SIZE];

/* Memory accessing interfaces */

uint32_t paddr_read(paddr_t addr, int len) {
  if(is_mmio(addr) == -1)
  {
    return pmem_rw(addr, uint32_t) & (~0u >> ((4 - len) << 3));
  }
  else
  {
    return mmio_read(addr, len, is_mmio(addr));
  }
}

void paddr_write(paddr_t addr, int len, uint32_t data) {
  if(is_mmio(addr) == -1)
  {
    memcpy(guest_to_host(addr), &data, len);;
  }
  else
  {
    mmio_write(addr, len, data, is_mmio(addr));
  }
}

paddr_t page_translate(vaddr_t vaddr, bool is_write) {
  // 确保分页机制已启用
  //CR0 cr0 = (CR0)cpu.CR0;
  //assert(cr0.paging);

  // 获取页目录基址
  CR3 cr3 = (CR3)cpu.CR3;
  paddr_t pdir_base = PTE_ADDR(cr3.val); 

  // 解析 PDE
  uint32_t pde_idx = PDX(vaddr);
  paddr_t pde_addr = pdir_base + pde_idx * sizeof(PDE);
  PDE pde;
  pde.val = paddr_read(pde_addr, sizeof(PDE));

  // 检查 PDE 的 present 位
  //assert(pde.present); // 表项不存在，终止运行

  // 更新 PDE 的 accessed 位
  if (!pde.accessed) {
    pde.accessed = 1;
    paddr_write(pde_addr, sizeof(PDE), pde.val);
  }

  // 获取页表基址
  paddr_t ptable_base = PTE_ADDR(pde.val); 

  // 解析 PTE
  uint32_t pte_idx = PTX(vaddr);
  paddr_t pte_addr = ptable_base + pte_idx * sizeof(PTE);
  PTE pte;
  pte.val = paddr_read(pte_addr, sizeof(PTE));

  // 检查 PTE 的 present 位
  //assert(pte.present); // 表项不存在，终止运行

  // 更新 PTE 的 accessed 和 dirty 位
  bool updated = false;
  if (!pte.accessed) {
    pte.accessed = 1;
    updated = true;
  }
  if (is_write && !pte.dirty) {
    pte.dirty = 1;
    updated = true;
  }
  if (updated) {
    paddr_write(pte_addr, sizeof(PTE), pte.val);
  }

  // 组合物理地址
  paddr_t paddr = PTE_ADDR(pte.val) | OFF(vaddr); 
  return paddr;
}

uint32_t vaddr_read(vaddr_t addr, int len) {
  // 处理跨页边界访问
  if ((addr & PAGE_MASK) + len > PAGE_SIZE) {
    // 计算两部分长度
    int part1_len = PAGE_SIZE - (addr & PAGE_MASK);
    int part2_len = len - part1_len;
    assert(part1_len > 0 && part2_len > 0);

    // 读取第一部分数据
    paddr_t paddr1 = page_translate(addr, false);
    uint32_t data1 = paddr_read(paddr1, part1_len);

    // 读取第二部分数据
    vaddr_t addr2 = addr + part1_len;
    paddr_t paddr2 = page_translate(addr2, false);
    uint32_t data2 = paddr_read(paddr2, part2_len);

    return (data2 << (part1_len * 8)) | data1;
  } 
  else {
    CR0 cr0 = (CR0)cpu.CR0;
    if (cr0.paging) {
      paddr_t paddr = page_translate(addr, false); // false 表示读操作
      return paddr_read(paddr, len);
    } else {
      return paddr_read(addr, len);
    }
  }
}

void vaddr_write(vaddr_t addr, int len, uint32_t data) {
  // 处理跨页边界访问
  if ((addr & PAGE_MASK) + len > PAGE_SIZE) {
    int part1_len = PAGE_SIZE - (addr & PAGE_MASK);
    int part2_len = len - part1_len;
    assert(part1_len > 0 && part2_len > 0);

    // 写入第一部分数据
    paddr_t paddr1 = page_translate(addr, true);
    paddr_write(paddr1, part1_len, data & ((1 << (part1_len * 8)) - 1));

    // 写入第二部分数据
    vaddr_t addr2 = addr + part1_len;
    paddr_t paddr2 = page_translate(addr2, true);
    paddr_write(paddr2, part2_len, data >> (part1_len * 8));
  } 
  else {
    CR0 cr0 = (CR0)cpu.CR0;
    if (cr0.paging) {
      paddr_t paddr = page_translate(addr, true); // true 表示写操作
      paddr_write(paddr, len, data);
    } else {
      paddr_write(addr, len, data);
    }
  }
}
